﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SharpSoft.Documentation.Html.CSS
{
    /// <summary>
    /// 选择器解释器
    /// </summary>
    public class SelectorParser
    {
        public SelectorParser(string source)
        {
            this._Lexer = new SelectorLexer(source);
        }

        public SelectorParser(SharpSoft.Lexing.TextSource source)
        {
            this._Lexer = new SelectorLexer(source);
        }

        private SelectorLexer _Lexer;
        /// <summary>
        /// 获取当前的词法解析器
        /// </summary>
        internal SelectorLexer Lexer
        {
            get { return _Lexer; }
        }

        SelectorToken[] Tokens;
        int position = 0;
        bool EOS()
        {
            return position >= Tokens.Length;
        }
        SelectorToken Current
        {
            get
            {
                if (EOS())
                    return SelectorToken.EndToken;
                return this.Tokens[position];
            }
        }
        SelectorToken Next(int offset = 1)
        {
            position += offset;
            return Current;
        }
        SelectorToken Peek(int offset = 1)
        {
            int p = position + offset;
            if (p >= Tokens.Length)
                return SelectorToken.EndToken;
            return this.Tokens[p];
        }
        /// <summary>
        /// 跳过空白并返回下个不为空白的标记
        /// </summary>
        SelectorToken SkipWhiteSpace()
        {
            SelectorToken t = Current;
            while (t.Type == SelectorTokenType.Progeny)
            {
                t = Next();
            }
            return t;
        }
        /// <summary>
        /// 跳过紧跟在当前标记之后的空白,调用此方法游标至少会前进一位，慎用。
        /// </summary>
        SelectorToken SkipLastWhiteSpace()
        {
            SelectorToken t = Next();
            while (t.Type == SelectorTokenType.Progeny)
            {
                t = Next();
            }
            return t;
        }
        /// <summary>
        /// 获取下一个不为空的标记(一般在当前标记为空时调用此方法)
        /// </summary>
        /// <returns></returns>
        SelectorToken PeekNotWhiteToken()
        {
            SelectorToken token = Current;
            int p = 0;
            while (token.Type == SelectorTokenType.Progeny)
            {
                token = Peek(++p);
            }
            return token;
        }

        /// <summary>
        /// 指针前进到下一个指定的标记类型处,直到遇到此类型的标记或源结束
        /// </summary>
        /// <param name="t">要跳到的下一个标记类型</param>
        void Next(SelectorTokenType t)
        {
            SelectorToken token = Current;
            while (token.Type != t && token.Type != SelectorTokenType.End)
            {
                token = Next();
            }
        }

        public CssSelector[] Parse()
        {
            List<SelectorToken> list = new List<SelectorToken>();
            Lexer.SkipWhiteSpace();
            SelectorToken token = Lexer.ReadToken();
            while (token.Type != SelectorTokenType.End)
            {
                list.Add(token);
                token = Lexer.ReadToken();
            }
            Tokens = list.ToArray();

            List<CssSelector> selectors = new List<CssSelector>();
            SelectorBuilder sb = new SelectorBuilder();
            SkipWhiteSpace();//开始解释前跳过选择器之前的空白
            token = Current;
            SelectorToken p = Peek();
            SelectorTokenType t = token.Type;
            SelectorTokenType tn = p.Type;//下一个标记的类型
            while (token.Type != SelectorTokenType.End)
            {
                token = Current; p = Peek(); t = token.Type; tn = p.Type;
                if (t == SelectorTokenType.Frame)
                {//框架规则标记.之后必须是Literal,否则抛出异常。
                    if (tn == SelectorTokenType.Literal)
                        sb.AddFrameRule(Next().Content);
                    else
                        throw new CssSyntaxException(p.Position, p.Length, "框架规则标识.之后必须是描述框架名称的常规量。");
                }
                else if (t == SelectorTokenType.Class)
                {//类名规则标记.之后必须是Literal,否则抛出异常。
                    if (tn == SelectorTokenType.Literal)
                        sb.AddClassRule(Next().Content);
                    else
                        throw new CssSyntaxException(p.Position, p.Length, "类名规则标识.之后必须是描述类名称常规量。");
                }
                else if (t == SelectorTokenType.Id)
                {//Id规则标记#之后必须是Literal,否则抛出异常。
                    if (tn == SelectorTokenType.Literal)
                    {
                        sb.AddIdRule(Next().Content);
                    }
                    else
                        throw new CssSyntaxException(p.Position, p.Length, "Id规则标识#之后必须是描述Id名称常规量。");
                }
                else if (t == SelectorTokenType.PseudoClass)
                {//伪类规则:之后必须是Literal,否则抛出异常
                    if (tn == SelectorTokenType.Literal)
                    {
                        string name = Next().Content;
                        string para = null;
                        p = Peek();
                        if (p.Type == SelectorTokenType.PseudoClassParameter)//伪类参数
                            para = Next().Content;
                        sb.AddPseudoClassRule(name, para);
                    }
                    else
                        throw new CssSyntaxException(p.Position, p.Length, "Id规则标识#之后必须是描述Id名称常规量。");

                }
                else if (t == SelectorTokenType.OpenBracket)
                {//属性规则标记[之后必须是Literal,否则抛出异常。
                    if (tn == SelectorTokenType.Literal)
                    {
                        sb.AddRule(ReadAttributeRule());
                    }
                    else
                        throw new CssSyntaxException(p.Position, p.Length, "属性规则标记[之后必须是描述属性名称的常规量。");
                }
                else if (t == SelectorTokenType.Literal)
                {//独立的Literal当作TagName
                    sb.AddTagRule(token.Content);
                }//****************处理规则之间的关系****************
                else if (t == SelectorTokenType.Progeny)
                {//遇到空白
                    token = SkipWhiteSpace();//跳至下一个不为空的标记
                    switch (token.Type)
                    {
                        case SelectorTokenType.Child:
                        case SelectorTokenType.NextOne:
                        case SelectorTokenType.NextN:
                            break;//DoNothing……
                        default://是其他标记，可以确定此空白是后代标记
                            sb.SetRuleRelation(CssSelectorRuleRelationType.Progeny);
                            break;
                    } 
                    continue;//跳过循环末的Next()
                }
                else if (t == SelectorTokenType.Child)
                {//子代标记
                    sb.SetRuleRelation(CssSelectorRuleRelationType.Child);
                    token = SkipLastWhiteSpace();//跳过之后的全部空白
                    continue;
                }
                else if (t == SelectorTokenType.NextOne)
                {
                    sb.SetRuleRelation(CssSelectorRuleRelationType.NextOne);
                    token = SkipLastWhiteSpace();
                    continue;
                }
                else if (t == SelectorTokenType.NextN)
                {
                    sb.SetRuleRelation(CssSelectorRuleRelationType.NextN);
                    token = SkipLastWhiteSpace();
                    continue;
                }//*************************************************
                else if (t == SelectorTokenType.Comma)
                {//当前选择器完结。写入缓存，开始下一个。
                    selectors.Add(sb.GetSelector());
                    sb.Clear();
                }
                else//不可识别的标记，抛出异常
                {
                    throw new CssSyntaxException(token.Position, token.Length, "意外的标记。");
                }
                token = Next();
            }
            selectors.Add(sb.GetSelector());
            sb.Clear();
            return selectors.ToArray();
        }

        public static CssSelector[] Parse(string source)
        {
            SelectorParser p = new SelectorParser(source);
            return p.Parse();
        }

        private AttributeRule ReadAttributeRule()
        {
            SelectorToken token = Current;
            if (token.Type != SelectorTokenType.OpenBracket)
                throw new Exception("无法读取属性规则。");
            string attname = null;
            string attvalue = null;
            AttributeMatchType matchtype = AttributeMatchType.Equal;
            token = Next();
            SelectorToken p = Peek();
            if (token.Type == SelectorTokenType.Literal)
            {
                attname = token.Content;
                switch (p.Type)
                {
                    case SelectorTokenType.MatchTypeEqual:
                        matchtype = AttributeMatchType.Equal;
                        break;
                    case SelectorTokenType.MatchTypeItem:
                        matchtype = AttributeMatchType.Item;
                        break;
                    case SelectorTokenType.MatchTypeStartWith:
                        matchtype = AttributeMatchType.StartWith;
                        break;
                    case SelectorTokenType.MatchTypeEndWith:
                        matchtype = AttributeMatchType.EndWith;
                        break;
                    case SelectorTokenType.MatchTypeContains:
                        matchtype = AttributeMatchType.Contains;
                        break;
                    case SelectorTokenType.MatchTypeSpecific:
                        matchtype = AttributeMatchType.Specific;
                        break;
                    case SelectorTokenType.CloseBracket:
                        Next(2);
                        return new AttributeRule(attname);
                    default:
                        throw new CssSyntaxException(p.Position, p.Length, "应为属性规则的结束标识]或属性匹配符号。");
                }
                token = Next(2);
                if (token.Type == SelectorTokenType.String)
                    attvalue = token.Content;
                else if (token.Type == SelectorTokenType.Literal)
                    attvalue = token.Content;
                else
                    throw new CssSyntaxException(token.Position, token.Length, "应为描述属性值的字符串。");
                token = Next();
                if (token.Type != SelectorTokenType.CloseBracket)
                    throw new CssSyntaxException(token.Position, token.Length, "属性规则未正确闭合。");
                //Next();//跳过闭合标识
                return new AttributeRule(attname, attvalue, matchtype);
            }
            else
            {
                throw new CssSyntaxException(token.Position, token.Length, "此处应为属性名称标识符。");
            }
        }
        ///// <summary>
        ///// 读取规则之间的关系
        ///// </summary>
        ///// <returns></returns>
        //private SelectorRuleRelationType ReadRuleRelation()
        //{
        //    SelectorToken token = Current;

        //}

    }
}
